import { Vector3, Euler, Raycaster } from 'three'

/**
 * 判断附近有没有障碍物 返回相机第一个物体,false
 * objects 场景中的障碍物数组
 * position 射线起点
 * thresholdDistance 判断障碍物的距离
 */
const raycaster = new Raycaster()
function collideCheck(objects, position, thresholdDistance = 1) {
	// 定义多个方向
	const directions = [
		new Vector3(1, 0, 0), // x 轴正方向
		new Vector3(-1, 0, 0), // x 轴负方向
		new Vector3(0, 1, 0), // y 轴正方向
		new Vector3(0, -1, 0), // y 轴负方向
		new Vector3(0, 0, 1), // z 轴正方向
		new Vector3(0, 0, -1) // z 轴负方向
	]
	// 遍历多个方向
	directions.forEach((dir) => {
		// 将方向向量归一化
		dir.normalize()
		// 设置射线起点和方向
		raycaster.set(position, dir)
		// 检测射线与障碍物的相交
		const intersects = raycaster.intersectObjects(objects, false)
		if (intersects.length > 0 && intersects[0].distance < thresholdDistance) {
			return intersects[0]
		}
	})
	return false
}

/**
 * 第一人称漫游视角，具备碰撞检测功能
 */
class FirstPersonControls {
	isFirstPersonControls = true
	constructor(camera, domElement, obstacles = []) {
		// 相机
		this.camera = camera
		this.domElement = domElement
		this.domElement.style.touchAction = 'none' // disable touch scroll
		// 障碍物数组
		this.obstacles = obstacles

		let up = false, //向前运行
			down = false, //向后运行
			left = false, //向左运行
			right = false //向右运行

		this.keyType = '' // 按钮类型
		this.domElement.addEventListener('keydown', keydownFn, false)
		this.domElement.addEventListener('keyup', keyupFn, false)

		const scope = this
		function keydownFn(event) {
			switch (event.keyCode) {
				case 38: // up
				case 87: // w
					up = true
					keyType = 'up'
					break
				case 37: // left
				case 65: // a
					left = true
					keyType = 'left'
					break
				case 40: // down
				case 83: // s
					down = true
					keyType = 'down'
					break
				case 39: // right
				case 68: // d
					right = true
					keyType = 'right'
					break
			}
		}
		function keyupFn(event) {
			keyType = ''
			switch (event.keyCode) {
				case 38: // up
				case 87: // w
					up = false
					break
				case 37: // left
				case 65: // a
					left = false
					break
				case 40: // down
				case 83: // s
					down = false
					break
				case 39: // right
				case 68: // d
					right = false
					break
			}
		}
		// 相机移动(碰撞检测)
		let direction = new Vector3()
		this.update = function (objects = scope.obstacles, speed = 0.2) {
			camera.getWorldDirection(direction)
			direction.y = 0
			let position = camera.position.clone()
			if (up) {
				position.add(direction.multiplyScalar(speed))
			}
			if (down) {
				position.add(direction.multiplyScalar(-speed))
			}
			if (left) {
				direction.cross(camera.up)
				position.add(direction.multiplyScalar(-speed))
			}
			if (right) {
				direction.cross(camera.up)
				position.add(direction.multiplyScalar(speed))
			}
			if (!collideCheck(objects, position)) {
				camera.position.set(position.x, position.y, position.z)
			}
		}

		//相机旋转
		let isClicked = false,
			minPolarAngle = 0, // radians
			maxPolarAngle = Math.PI, // radians
			pointerSpeed = 1.0
		const _PI_2 = Math.PI / 2,
			// 欧拉角描述一个旋转变换，通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体。
			_euler = new Euler(0, 0, 0, 'YXZ')

		this.domElement.addEventListener('mousedown', mousedown, false)
		this.domElement.addEventListener('mouseup', mouseup, false)
		this.domElement.addEventListener('mousemove', MouseMove, false)
		function mousedown(event) {
			isClicked = true
		}
		function mouseup(event) {
			isClicked = false
		}
		//相机旋转
		function MouseMove(event) {
			event.preventDefault()
			if (!isClicked) return
			const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0
			const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0
			_euler.setFromQuaternion(scope.camera.quaternion)
			_euler.y -= movementX * 0.002 * pointerSpeed
			_euler.x += movementY * 0.002 * pointerSpeed
			_euler.x = Math.max(_PI_2 - maxPolarAngle, Math.min(_PI_2 - minPolarAngle, _euler.x))
			scope.camera.quaternion.setFromEuler(_euler)
		}
		this.dispose = function () {
			this.domElement.removeEventListener('keydown', keydownFn)
			this.domElement.removeEventListener('keyup', keyupFn)
			documthis.domElementent.removeEventListener('mousedown', mousedown)
			this.domElement.removeEventListener('mouseup', mouseup)
			this.domElement.removeEventListener('mousemove', MouseMove)
			this.obstacles = []
		}
	}
}

export { raycaster, collideCheck, FirstPersonControls }
